#include <stdio.h>
#include <execinfo.h>
#include <unistd.h>
#include "userLog.h"
#include "CxlUtil.h"
#include <sys/time.h>
#include <stdlib.h>
#include <errno.h>  // errno
#include <string.h> // strerror
#include "configParser.h"
#include "hexStringCov.h"
#include "cJSON.h"
#include "sqlLoop.h"

static uint8_t BcdFromHex(const uint8_t src);
static int set_time_to_system(AppTime *sys_time);
static int32_t get_time_from_system(AppTime *sys_time);
static uint8_t hex2char(uint8_t c);

void bt()
{
#define STACK_SIZE 32
    void *trace[STACK_SIZE];
    size_t size = backtrace(trace, STACK_SIZE);
    uint8_t **symbols = (uint8_t **)backtrace_symbols(trace, size);
    LOG_ERROR("打印函数栈：");
    size_t i;
    for (i = 0; i < size; i++)
    {
        LOG_ERROR("%ld--->%s", i, symbols[i]);
    }
}

static uv_loop_t *sqlLoop;

static uv_loop_t *loop;
/*
*@描述：删除图片用.
*/
static uv_loop_t *pic_delete_loop;

void initLoop()
{
    loop = (uv_loop_t *)malloc(sizeof(uv_loop_t));
    uv_loop_init(loop);

    sqlLoop = (uv_loop_t *)malloc(sizeof(uv_loop_t));
    uv_loop_init(sqlLoop);

    pic_delete_loop = (uv_loop_t *)malloc(sizeof(uv_loop_t));
    uv_loop_init(pic_delete_loop);
}

uv_loop_t *getSqlLoop()
{
    return sqlLoop;
}

uv_loop_t *getLoop()
{
    return loop;
}

void alloc_buffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf)
{
    buf->base = (uint8_t *)malloc(suggested_size);
    memset(buf->base, 0, suggested_size);
    buf->len = suggested_size;
}

void free_write_req(uv_write_t *req)
{
    // LOG_DEBUG("释放地址： %p", req);
    write_req_t *wr = (write_req_t *)req;
    free(wr->buf.base);
    free(wr);
}

#define POLY 0x1021
uint16_t crc(uint8_t *addr, uint32_t num)
{
    uint16_t crc_ret = 0;
    uint32_t i;
    for (; num > 0; num--) /* Step through bytes in memory */
    {
        crc_ret = crc_ret ^ (*addr++ << 8); /* Fetch byte from memory, XOR into CRC top byte*/
        for (i = 0; i < 8; i++)             /* Prepare to rotate 8 bits */
        {
            if (crc_ret & 0x8000)                /* b15 is set... */
                crc_ret = (crc_ret << 1) ^ POLY; /* rotate and XOR with polynomic */
            else                                 /* b15 is clear... */
                crc_ret <<= 1;                   /* just rotate */
        }                                        /* Loop for 8 bits */
        crc_ret &= 0xFFFF;                       /* Ensure CRC remains 16-bit value */
    }                                            /* Loop until num=0 */
    return (crc_ret);                            /* Return updated CRC */
}

int8_t calculate_file_crc(uint8_t *file_path, uint16_t *crc_p)
{
    FILE *fd = fopen(file_path, "rb+");
    if (NULL == fd)
    {
        LOG_ERROR("crc错误：读文件失败");
        return FILE_CRC_ERROR;
    }
    fseek(fd, 0, SEEK_END);
    uint32_t file_size = ftell(fd);
    fseek(fd, 0, SEEK_SET);
    uint8_t *buf = (uint8_t *)malloc(file_size);

    int32_t a = fread(buf, sizeof(uint8_t), file_size, fd);
    *crc_p = crc(buf, file_size);
    free(buf);
    fclose(fd);
    return FILE_CRC_SUCCESS;
}

uint8_t BcdFromHex(const uint8_t src)
{
    uint8_t temp;

    temp = ((src / 10) << 4) + (src % 10);

    return temp;
}

void time_bcd(uint8_t data[BCD_TIME_LENGTH])
{
    //YYMMDDHHMMSS
    struct tm tm;
    time_t t;
    time(&t);
    localtime_r(&t, &tm);
    time_bcd_from_time_t(t, data);
    // LOG_DEBUG("date: %02x %02x%02x %02x:%02x:%02x", data[0], data[1], data[2], data[3], data[4], data[5]);
}

void time_bcd_from_time_t(time_t t, uint8_t data[BCD_TIME_LENGTH])
{
    //YYMMDDHHMMSS
    struct tm tm;
    localtime_r(&t, &tm);
    data[0] = (uint8_t)(((tm.tm_year + 1900) % 100));
    data[1] = tm.tm_mon + 1;
    data[2] = tm.tm_mday;
    data[3] = tm.tm_hour;
    data[4] = tm.tm_min;
    data[5] = tm.tm_sec;
    uint8_t i;
    for (i = 0; i < 6; i++)
    {
        data[i] = BcdFromHex(data[i]);
    }
    // LOG_DEBUG("date: %02x %02x%02x %02x:%02x:%02x", data[0], data[1], data[2], data[3], data[4], data[5]);
}

void get_time_value(const int8_t *buf, AppTime *sys_time)
{
    int8_t tmp_buf[32] = {0};
    sprintf(tmp_buf, "%.2X%.2X%.2X%.2X%.2X%.2X",
            buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]);
    sscanf(tmp_buf,
           "%2d%2d%2d%2d%2d%2d",
           &sys_time->tm_year, &sys_time->tm_mon,
           &sys_time->tm_mday, &sys_time->tm_hour,
           &sys_time->tm_min, &sys_time->tm_sec);
    sys_time->tm_year = sys_time->tm_year + 2000;
    // LOG_INFO("[set_time]:%04d-%02d-%02d %02d:%02d:%02d",
    //     sys_time->tm_year, sys_time->tm_mon, sys_time->tm_mday,
    //     sys_time->tm_hour, sys_time->tm_min, sys_time->tm_sec);
    return;
}

static int set_time_to_system(AppTime *sys_time)
{
    struct tm _tm;
    time_t timep;

    // LOG_INFO("[set_time]:%04d-%02d-%02d %02d:%02d:%02d",
    //          sys_time->tm_year, sys_time->tm_mon, sys_time->tm_mday,
    //  sys_time->tm_hour, sys_time->tm_min, sys_time->tm_sec);

    _tm.tm_sec = sys_time->tm_sec;
    _tm.tm_min = sys_time->tm_min;
    _tm.tm_hour = sys_time->tm_hour;
    _tm.tm_mday = sys_time->tm_mday;
    _tm.tm_mon = sys_time->tm_mon - 1;
    _tm.tm_year = sys_time->tm_year - 1900;

    _tm.tm_isdst = -1;

    timep = mktime(&_tm);
    if (stime(&timep) < 0)
    {
        LOG_INFO("Set system time error! %s", strerror(errno));
        return -1;
    }

    return 0;
}

static int32_t get_time_from_system(AppTime *sys_time)
{
    time_t timep;
    struct tm *p;
    time(&timep);
    p = gmtime(&timep);
    sys_time->tm_year = p->tm_year + 1900;
    sys_time->tm_mon = p->tm_mon + 1;
    sys_time->tm_mday = p->tm_mday;

    sys_time->tm_hour = p->tm_hour;
    sys_time->tm_min = p->tm_min;
    sys_time->tm_sec = p->tm_sec;
    // LOG_INFO("[get_time]:%04d-%02d-%02d %02d:%02d:%02d",
    //          sys_time->tm_year, sys_time->tm_mon, sys_time->tm_mday,
    //          sys_time->tm_hour, sys_time->tm_min, sys_time->tm_sec);
    return 0;
}

int32_t chedaoji_set_time(AppTime *sys_time)
{
    //  LOG_INFO("[set_time]:%04d-%02d-%02d %02d:%02d:%02d",
    //          sys_time->tm_year, sys_time->tm_mon, sys_time->tm_mday,
    //          sys_time->tm_hour, sys_time->tm_min, sys_time->tm_sec);
    int32_t ret = 0;
    AppTime local_time;
    memset(&local_time, 0, sizeof(local_time));
    get_time_from_system(&local_time);
    if (sys_time->tm_year != local_time.tm_year ||
        sys_time->tm_mon != local_time.tm_mon ||
        sys_time->tm_mday != local_time.tm_mday ||
        sys_time->tm_hour != local_time.tm_hour ||
        sys_time->tm_min != local_time.tm_min ||
        (abs(sys_time->tm_sec - local_time.tm_sec) >= 2))
    {
        ret = set_time_to_system(sys_time);
    }
    return ret;
}

bool setOpt(int fd, int nSpeed, int nBits, int nParity, int nStop)
{
    struct termios newtio, oldtio;

    // 保存测试现有串口参数设置，在这里如果串口号等出错，会有相关的出错信息
    if (tcgetattr(fd, &oldtio) != 0)
    {
        LOG_ERROR("设置串口参数错误：%s", strerror(errno));
        return false;
    }

    bzero(&newtio, sizeof(newtio));   //新termios参数清零
    newtio.c_cflag |= CLOCAL | CREAD; //CLOCAL--忽略 modem 控制线,本地连线, 不具数据机控制功能, CREAD--使能接收标志
    // 设置数据位数
    newtio.c_cflag &= ~CSIZE; //清数据位标志
    switch (nBits)
    {
    case 7:
        newtio.c_cflag |= CS7;
        break;
    case 8:
        newtio.c_cflag |= CS8;
        break;
    default:
        LOG_ERROR("串口错误：不支持的数据大小");
        return false;
    }
    // 设置校验位
    switch (nParity)
    {
    case 'o':
    case 'O': //奇校验
        newtio.c_cflag |= PARENB;
        newtio.c_cflag |= PARODD;
        newtio.c_iflag |= (INPCK | ISTRIP);
        break;
    case 'e':
    case 'E': //偶校验
        newtio.c_iflag |= (INPCK | ISTRIP);
        newtio.c_cflag |= PARENB;
        newtio.c_cflag &= ~PARODD;
        break;
    case 'n':
    case 'N': //无校验
        newtio.c_cflag &= ~PARENB;
        break;
    default:
        LOG_ERROR("串口错误：不支持的奇偶校验");
        return false;
    }
    // 设置停止位
    switch (nStop)
    {
    case 1:
        newtio.c_cflag &= ~CSTOPB;
        break;
    case 2:
        newtio.c_cflag |= CSTOPB;
        break;
    default:
        LOG_ERROR("串口错误：不支持的停止bit位");
        return false;
    }
    // 设置波特率 2400/4800/9600/19200/38400/57600/115200/230400
    switch (nSpeed)
    {
    case 2400:
        cfsetispeed(&newtio, B2400);
        cfsetospeed(&newtio, B2400);
        break;
    case 4800:
        cfsetispeed(&newtio, B4800);
        cfsetospeed(&newtio, B4800);
        break;
    case 9600:
        cfsetispeed(&newtio, B9600);
        cfsetospeed(&newtio, B9600);
        break;
    case 19200:
        cfsetispeed(&newtio, B19200);
        cfsetospeed(&newtio, B19200);
        break;
    case 38400:
        cfsetispeed(&newtio, B38400);
        cfsetospeed(&newtio, B38400);
        break;
    case 57600:
        cfsetispeed(&newtio, B57600);
        cfsetospeed(&newtio, B57600);
        break;
    case 115200:
        cfsetispeed(&newtio, B115200);
        cfsetospeed(&newtio, B115200);
        break;
    case 230400:
        cfsetispeed(&newtio, B230400);
        cfsetospeed(&newtio, B230400);
        break;
    default:
        LOG_INFO("不支持的波特率，设置为默认的9600波特率");
        cfsetispeed(&newtio, B9600);
        cfsetospeed(&newtio, B9600);
        break;
    }
    // 设置read读取最小字节数和超时时间
    newtio.c_cc[VTIME] = 1; // 读取一个字符等待1*(1/10)s
    newtio.c_cc[VMIN] = 1;  // 读取字符的最少个数为1

    tcflush(fd, TCIFLUSH);                    //清空缓冲区
    if (tcsetattr(fd, TCSANOW, &newtio) != 0) //激活新设置
    {
        LOG_ERROR("设置串口参数2错误：%s", strerror(errno));
        return false;
    }
    LOG_INFO("设置串口成功");
    return true;
}

/**千寻差分定位**/
static const char encodingTable[64] = {
    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
    'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
    'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
    'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
/* does not buffer overrun, but breaks directly after an error */
/* returns the number of required bytes */
int encode(char *buf, int size, const char *user, const char *pwd)
{
    unsigned char inbuf[3];
    char *out = buf;
    int i, sep = 0, fill = 0, bytes = 0;

    while (*user || *pwd)
    {
        i = 0;
        while (i < 3 && *user)
            inbuf[i++] = *(user++);
        if (i < 3 && !sep)
        {
            inbuf[i++] = ':';
            ++sep;
        }
        while (i < 3 && *pwd)
            inbuf[i++] = *(pwd++);
        while (i < 3)
        {
            inbuf[i++] = 0;
            ++fill;
        }
        if (out - buf < size - 1)
            *(out++) = encodingTable[(inbuf[0] & 0xFC) >> 2];
        if (out - buf < size - 1)
            *(out++) = encodingTable[((inbuf[0] & 0x03) << 4) | ((inbuf[1] & 0xF0) >> 4)];
        if (out - buf < size - 1)
        {
            if (fill == 2)
                *(out++) = '=';
            else
                *(out++) = encodingTable[((inbuf[1] & 0x0F) << 2) | ((inbuf[2] & 0xC0) >> 6)];
        }
        if (out - buf < size - 1)
        {
            if (fill >= 1)
                *(out++) = '=';
            else
                *(out++) = encodingTable[inbuf[2] & 0x3F];
        }
        bytes += 4;
    }
    if (out - buf < size)
        *out = 0;
    return bytes;
}

#define FILE_NAME_TIME_STR_LEN 13
int32_t bcd_time_to_file_name_string(const uint8_t bcd[BCD_TIME_LENGTH], uint8_t dst[FILE_NAME_TIME_STR_LEN])
{
    uint8_t tmp[BCD_TIME_LENGTH * 2 + 1] = {0};
    int32_t index = 0;
    int32_t tmp_index = 0;
    for (index = 0; index < BCD_TIME_LENGTH; index++)
    {
        tmp[tmp_index++] = hex2char((bcd[index] >> 4) & 0xf);
        tmp[tmp_index++] = hex2char((bcd[index]) & 0xf);
    }
    int32_t yy, mm, dd;
    int32_t hh, min, sec;
    if (BCD_TIME_LENGTH !=
        sscanf(tmp, "%02d%02d%02d%02d%02d%02d",
               &yy, &mm, &dd, &hh, &min, &sec))
    {
        LOG_ERROR("sscanf fail, tmp:%s", tmp);
        return -1;
    }

    sprintf(dst, "%02d%02d%02d%02d%02d%02d",
            yy, mm, dd, hh, min, sec);
    return 0;
}

// 生成图片名字，返回该文件名的文件块编号
void genPicFileName(uint8_t fileName[PIC_FILENAME_MAX_LEN], uint8_t uniqueTag, uint8_t timeBcd[BCD_TIME_LENGTH], uint8_t camId, uint8_t picType)
{
    uint8_t uniqueTagStr[2 + 1] = {0};
    uint8_t camIdStr[2 + 1] = {0};
    uint8_t picTypeUint8Str[2 + 1] = {0};
    uint8_t CheDaoJi_No_Str[CheDaoJi_No_Len * 2 + 1] = {0};

    uint8_t picTypeUint8 = picType;
    // YYMMDDHHMMSS
    unsigned char timeStr[FILE_NAME_TIME_STR_LEN] = {0};

    bcd_time_to_file_name_string(timeBcd, timeStr);
    hexToStr(uniqueTagStr, &uniqueTag, sizeof(uniqueTag));
    hexToStr(picTypeUint8Str, &picTypeUint8, sizeof(picTypeUint8));
    hexToStr(camIdStr, &camId, sizeof(camId));
    hexToStr(CheDaoJi_No_Str, getChedaojiNo(), CheDaoJi_No_Len);
    sprintf(fileName, "%s%s%s%s%s", CheDaoJi_No_Str, timeStr, uniqueTagStr, camIdStr, picTypeUint8Str);

    // LOG_DEBUG("文件名：%s，文件名长度%ld", fileName, strlen(fileName));
}

uint32_t getFilesNum(uint8_t *path)
{
    DIR *dir;
    uint32_t fileNum = 0;
    struct dirent *ptr;
    int32_t i;
    dir = opendir(path);
    if (dir == NULL)
    {
        LOG_ERROR("打开目录:%s失败, 报错:%s", path, strerror(errno));
        return 51;
    }
    while ((ptr = readdir(dir)) != NULL)
    {
        if (strcmp(ptr->d_name, ".") == 0 || strcmp(ptr->d_name, "..") == 0)
        {
            continue;
        }
        fileNum++;
    }
    closedir(dir);
    return fileNum;
}

bool mkdirCommon(uint8_t *path)
{
    if (access(path, F_OK) != 0)
    {
#if 0
        char buf[80];
        getcwd(buf, sizeof(buf));
        LOG_INFO("当前工作目录: %s\n", buf);
#endif
        if (mkdir(path, S_IRWXU) != 0)
        {
            LOG_ERROR("创建目录失败: %s", strerror(errno));
            return false;
        }
    }
    return true;
}

static uint8_t hex2char(uint8_t c)
{
    if (0 <= c && c <= 9)
    {
        return c + '0';
    }
    else if (10 <= c && c <= 15)
    {
        return c - 10 + 'A';
    }
    return '0';
}

/*
*@描述：把bcd表示的时间转成字符串形式.
*@输入：6字节bcd时间码
*@输入：dst存放转换后的字符串,需要保证存储空间
*@返回值:0成功，其他值失败
*/
int32_t bcd_time_to_string(const uint8_t bcd[BCD_TIME_LENGTH], uint8_t dst[DATE_TIME_STRING_LENGTH])
{
    uint8_t tmp[BCD_TIME_LENGTH * 2 + 1] = {0};
    int32_t index = 0;
    int32_t tmp_index = 0;
    for (index = 0; index < BCD_TIME_LENGTH; index++)
    {
        tmp[tmp_index++] = hex2char((bcd[index] >> 4) & 0xf);
        tmp[tmp_index++] = hex2char((bcd[index]) & 0xf);
    }
    int32_t yy, mm, dd;
    int32_t hh, min, sec;
    if (BCD_TIME_LENGTH !=
        sscanf(tmp, "%02d%02d%02d%02d%02d%02d",
               &yy, &mm, &dd, &hh, &min, &sec))
    {
        LOG_ERROR("sscanf fail, tmp:%s", tmp);
        return -1;
    }

    sprintf(dst, "%04d-%02d-%02d %02d:%02d:%02d",
            2000 + yy, mm, dd, hh, min, sec);
    return 0;
}

uv_loop_t *getPicDeleteLoop()
{
    return pic_delete_loop;
}

double getMemoryRate()
{
    double free_memory = uv_get_free_memory();
    double total_memory = uv_get_total_memory();
    double result = (total_memory - free_memory) / total_memory * 100;
    LOG_INFO("当前内存利用率:%d", (int)result);
    return result;
}

/*
*@描述:功能函数测试入口
*/
void test_func()
{

#if 0
    //测试时间bcd码转换
    char bcd_time[] = {0x21, 0x09, 0x10, 0x16, 0x04, 0x22};
    char dst[32] = {0};
    (void)bcd_time_to_string(bcd_time, dst);
    LOG_INFO("dst:%s", dst);
#endif

    return;
}
